Das Ziel ist es, aus dem Datacamp Datensatz Video Game Sales Date folgende Fragestellung / Hypothese zu beantworten:

Fragestellung: “Ist es wahrscheinlicher, dass sich bestimmte Spielgenres in Europa signifikant (Unterschied von 50%) besser verkaufen lassen als im Japanischen und Nordamerikanischen Markt?”

Als Einführung werden wir auf Datacamp folgende Kurse durchgehen:

# Import libraries
library("plotly")
library("ggplot2")
library("plyr")
library("dplyr")
library("broom")
library("gridExtra")   
# Read csv from folder "data"
df = read.csv("./data/video_games_data.csv")

head(df, 10)

Data Wrangling

Bevor wir mit den Visualisierungen und Modelle beginnen können, müssen wir die Daten säubern. Das heisst es sollte keine Duplikate geben, fehlende Werte sollten korrekt eingetragen werden und Daten, die nicht verwendet werden sollten gelöscht werden.

Es hat “N/A” Werte in den Spalten “Year” und “Publisher”. Diese Werte sollten korrekte “NA” Werte sein, damit sie bei den Visualisierungen und Berechnungen nicht berücksichtigt werden.

# Show rows with "N/A" values
df[grep("N/A", df$Publisher),]
df[grep("N/A", df$Year),]
# Replace "N/A" with "NA"
df[df == "N/A"] <- NA

df <- df %>%
  filter(df$Global_Sales > 0.1)

df
# Check if values have been converted
df %>% 
  summarize(across(everything(), ~sum(is.na(.))))

Da 2017 nur 3 Einträge und 2020 nur 1 Eintrag beinhaltet, werden wir diese Jahren nicht berücksichtigen und aus dem Dataframe löschen, weil sie nicht vollständig sind und so könnten unsere Modelle ungenau werden.

# Remove years 2017 and 2020 from dataset
df_clean <- df[!(df$Year == "2017" | df$Year == "2020"),]

# Remove unnecessary columns because they are not needed for our thesis
drop <- c("Rank")
df_clean <- df_clean[,!(names(df_clean) %in% drop)]

df_clean
# Set data to correct type
df_clean$Genre <- as.factor(df_clean$Genre)
df_clean$Year <- as.numeric(df_clean$Year)

Erste Plots erstellen

na <- sum(df_clean[, 'NA_Sales'], na.rm = TRUE)
eu <- sum(df_clean[, 'EU_Sales'], na.rm = TRUE)
jp <- sum(df_clean[, 'JP_Sales'], na.rm = TRUE)
o <- sum(df_clean[, 'Other_Sales'], na.rm = TRUE)
g <- sum(df_clean[, 'Global_Sales'], na.rm = TRUE)

fig <- plot_ly(
  y = c(na, eu, jp, o), 
  x = c("North America", "Europe", "Japan", "Other"), 
  type = 'bar',
  width = 800
)

fig <- fig %>% layout(title = "Video Game Sales Overview",
         xaxis = list(title = "Region"),
         yaxis = list(title = "Sales (million)"))

fig

Wir sehen, dass Nord Amerika der grösste Markt ist.

# Group by genre and summarize game sales to each region
df_genre <- df_clean %>%
  group_by(Genre) %>%
  summarize(
    NA_Sales_Sum = sum(NA_Sales),
    EU_Sales_Sum = sum(EU_Sales), 
    JP_Sales_Sum = sum(JP_Sales),
    Other_Sales_Sum = sum(Other_Sales),
    Global_Sales_Sum = sum(Global_Sales)
  )

# Plot grouped bar chart video game sales by genre
fig <- plot_ly(
  df_genre, y = ~Genre, x = ~NA_Sales_Sum, type = "bar", name = "North America", width = 1000, height = 800) %>% 
  add_trace(x = ~EU_Sales_Sum, name = "Europe") %>%
  add_trace(x = ~JP_Sales_Sum, name = "Japan") %>%
  add_trace(x = ~Other_Sales_Sum, name = "Other") %>%
  layout(
    title = "Video Game Sales by Genre",
    xaxis = list(title = "Sales (million)"),
    barmode = "group"
  )

fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations

Japan hat viel mehr Role-Play, Strategie und viel weniger Shooter und Action als die anderen Regionen

df_year <- df_clean %>%
  group_by(Year) %>%
  summarize(
    NA_Sales_Sum = sum(NA_Sales),
    EU_Sales_Sum = sum(EU_Sales), 
    JP_Sales_Sum = sum(JP_Sales),
    Other_Sales_Sum = sum(Other_Sales),
    Global_Sales_Sum = sum(Global_Sales)
  )

fig <- plot_ly(
  df_year, y = ~NA_Sales_Sum, x = ~Year, type = "bar", name = "North America", width = 900, height = 500) %>% 
  add_trace(y = ~EU_Sales_Sum, name = "Europe") %>%
  add_trace(y = ~JP_Sales_Sum, name = "Japan") %>%
  add_trace(y = ~Other_Sales_Sum, name = "Other") %>%
  layout(
    title = "Video Game from Sales by Year",
    xaxis = list(title = "Year"),
    yaxis = list(title = "Sales (million)"),
    barmode = "stack"
  )

fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
# Calculate the average of sales of each genre from each region
df_sales_avg <- df_clean %>%
  group_by(Genre) %>%
  summarise(
    EU_Sales_Avg = mean(EU_Sales),
    NA_Sales_Avg = mean(NA_Sales),
    JP_Sales_Avg = mean(JP_Sales),
    Other_Sales_Avg = mean(Other_Sales),
    Global_Sales_Avg = mean(Global_Sales))

df_sales_avg

Hier sehen wir die durchschnittliche Anzhal von Verkäufe aller Genre

Regressionsmodelle

Da wir unsere Daten jetzt besser verstehen, können wir mit den Regressionsmodellen und mit der Beantwortung unserer Fragestellung beginnen.

Kann man anhand der nordamerikanischen Verkäufe voraussagen, wie sich ein Genre im europäischen Markt verkaufen wird?

Da es einige Ausreisser gibt, z.B wenn sich ein Spiel in einer Region gar nicht verkauft wurde, haben wir einen Filter eingebaut, welche Sales unter 1.0 nicht berücksichtigt.

Unten haben wir die besten Fits genommen, da bei den anderen Spielgenres der r-squared Wert unter 50% lag. Dort ist also kein Zusammenhang zu erkennen.

# Create DataFrame only with Racing games
df_racing <- df_clean %>%
  filter(
    Genre == "Racing",
    NA_Sales > 1.00,
    EU_Sales > 1.00
  )

ggplot(df_racing, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Racing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_racing <- lm(EU_Sales ~ NA_Sales, data = df_racing)

# Extract model score
mdl_racing %>%
  glance() %>%
  pull(r.squared)
[1] 0.8686435
# Predict EU Sales for a Racing Game based on NA Sales
predict_racing <- tibble(NA_Sales = 5)
predict(mdl_racing, predict_racing)
       1 
3.891789 

Wenn ein Racing Game in Nord Amerika 5 Millionen Verkäufe aufweist, liegt die Verkaufs-Vorhersage für Europa bei rund 3.9 Millionen.

# Create DataFrame only with Role-Playing games
df_role <- df_clean %>%
  filter(
    Genre == "Role-Playing",
    NA_Sales > 1.00,
    EU_Sales > 1.00
  )

ggplot(df_role, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Role-Playing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_rp <- lm(EU_Sales ~ NA_Sales, data = df_role)

# Extract model score
mdl_rp %>%
  glance() %>%
  pull(r.squared)
[1] 0.8433709
# Predict EU Sales for a Role-Playing Game based on NA Sales
predict_rp <- tibble(NA_Sales = 5)
predict(mdl_rp, predict_rp)
       1 
3.451286 
# Create DataFrame only with Shooter games
df_shooter <- df_clean %>%
  filter(
    Genre == "Shooter",
    NA_Sales > 0.00,
    EU_Sales > 0.00
  )

# Create DataFrame only with Shooter games without Duck Hunt
df_shooter_no_duckhunt <- df_clean %>%
  filter(
    Genre == "Shooter",
    NA_Sales > 0.00,
    Name != "Duck Hunt",
    EU_Sales > 0.00
  )

# Create subplots
p1 <- ggplot(df_shooter, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Shooter Game Sales")

p2 <- ggplot(df_shooter_no_duckhunt, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Shooter Game Sales no Duck Hunt")

# Plot both plots side by side
grid.arrange(p1, p2, ncol = 2)  
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_shooter <- lm(EU_Sales ~ NA_Sales, data = df_shooter)

# Extract model score
mdl_shooter %>%
  glance() %>%
  pull(r.squared)
[1] 0.4244135
# Create linear model
mdl_shooter_no_duckhunt <- lm(EU_Sales ~ NA_Sales, data = df_shooter_no_duckhunt)

# Extract model score
mdl_shooter_no_duckhunt %>%
  glance() %>%
  pull(r.squared)
[1] 0.6858556

Wie wir hier anhand der R-Squared sehen, fitted das Modell viel besser ohne Duck Hunt. Dies liegt daran, dass dieses Spiel in Nord Amerika zusammen mit der Konsole verkauft wurde, nicht wie in Europa. Darum ist dies für uns einen Ausreisser und wir wollten diese Genre ohne Ausreisser visualisieren und modellieren.

Aber da das Modell auch ohne Duck Hunt nicht gut sehr gut ist, werden wir mit diesem Genre keine Vorhersagen durchführen.

# Create DataFrame only with Simulation games
df_sim <- df_clean %>%
  filter(
    Genre == "Simulation",
    NA_Sales > 1.00,
    EU_Sales > 1.00
)

ggplot(df_sim, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Simulation Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_sim <- lm(EU_Sales ~ NA_Sales, data = df_sim)

# Extract model score
mdl_sim %>%
  glance() %>%
  pull(r.squared)
[1] 0.9233418
# Predict EU Sales for a Simulation Game based on NA Sales
predict_sim <- tibble(NA_Sales = 5)
predict(mdl_sim, predict_sim)
       1 
5.474846 

Wenn ein Simulation Game in NA 5 Million Verkäufe aufweist, liegt die Vorhersage für EU bei 5.4 Millionen.

# Create DataFrame only with Sport games
df_sport <- df_clean %>%
  filter(
    Genre == "Sports",
    NA_Sales > 1.00,
    EU_Sales > 1.00
  )

ggplot(df_sport, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = "Sport Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_sport <- lm(EU_Sales ~ NA_Sales, data = df_sport)

# Extract model score
mdl_sport %>%
  glance() %>%
  pull(r.squared)
[1] 0.9488926
# Predict EU Sales for a Sport Game based on NA Sales
predict_sport <- tibble(NA_Sales = 5)
predict(mdl_sport, predict_sport)
       1 
4.019965 

Wenn ein Sport Game in NA 5 Million Verkäufe aufweist, liegt die Vorhersage für EU bei 4 Millionen.

Residuenanalyse (zum beurteilen ob das Modell gut ist)

  • Residuen erwartungswert von 0
  • Residuen sind voneinander unabhängig
  • Residuen sind normalverteilt
# Create Residual Scatterplot

df <- augment(mdl_racing)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Racing Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_racing, aes(x = mdl_racing$residuals)) + 
  geom_histogram(bins = 30) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Racing Genre") +
  xlab("residuals")

Das Histogramm ist nicht ganz genau wie eine Normalverteilung aber könnte trotzdem als eine akzeptiert sein.

# Create Residual Scatterplot

df <- augment(mdl_rp)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Role-Playing Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_role, aes(x = mdl_rp$residuals)) + 
  geom_histogram(bins = 30) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Role-Playing Genre") +
  xlab("residuals")

Das Histogramm ist nicht ganz genau wie eine Normalverteilung aber könnte trotzdem als eine akzeptiert sein.

# Create Residual Scatterplot

df <- augment(mdl_sim)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Simulation Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_sim, aes(x = mdl_sim$residuals)) + 
  geom_histogram(bins = 30) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Simulation Genre") +
  xlab("residuals")

Das Histogramm ist nicht ganz genau wie eine Normalverteilung aber könnte trotzdem als eine akzeptiert sein.

# Create Residual Scatterplot

df <- augment(mdl_sport)

ggplot(df, aes(x = 1:nrow(df), y = .resid)) + 
  geom_point() +
  geom_hline(yintercept=0, color="Red") +
  ggtitle("Residuals Model Sport Genre") +
  xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)

ggplot(df_sport, aes(x = mdl_sport$residuals)) + 
  geom_histogram(bins = 50) +
  geom_density(color = "Red") +
  ggtitle("Residuals Model Sport Genre") +
  xlab("residuals")

Das Histogramm ist nicht ganz genau wie eine Normalverteilung aber könnte trotzdem als eine akzeptiert sein.

Nintendo Spiele verkaufen sich besser als Electronic Arts auf der ganzen Welt.

Mit dieser Fragestellung wollen wir herausfinden ob sich die Nintendo Konsole in Japan besser verkauft.

# Create DataFrame with only Nintendo and Sony as Publisher
publishers = c("Nintendo", "Electronic Arts")

df_publisher <- df_clean %>%
  filter(
    Publisher %in% publishers
    )

df_publisher
# Replace publisher name with 0 and 1
df_publisher$Publisher[df_publisher$Publisher == "Electronic Arts"] <- 0
df_publisher$Publisher[df_publisher$Publisher == "Nintendo"] <- 1

# Save as int
df_publisher$Publisher <- as.numeric(df_publisher$Publisher)
# Create logistic model
mdl_publisher <- glm(Publisher ~ NA_Sales, data = df_publisher, family = binomial())
ggplot(df_publisher, aes(x=Global_Sales, y=Publisher)) + 
  geom_point(alpha=.5, color="Blue") +
  stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
  labs(
    x = "Sales (million)",
    y = "1=Nintendo / 0=Electronic Arts",
    title = "Probability that a game is from Nintendo based on global sales"
  )
`geom_smooth()` using formula 'y ~ x'

Wir können hier gut sehen, dass Nintendo viel erfolgreichere Spiele produziert haben. Dies deutet auch an, dass Nintendo beliebter ist als Electronic Arts

Wii Spiele verkaufen sich besser wie DS Spiele in Japan

# Create DataFrame with only Wii and DS as Platforms
platforms = c("Wii", "DS")

df_platform <- df_clean %>%
  filter(
    Platform %in% platforms
    )

df_platform
# Replace platform name with 0 and 1
df_platform$Platform[df_platform$Platform == "Wii"] <- 0
df_platform$Platform[df_platform$Platform == "DS"] <- 1

# Save as int
df_platform$Platform <- as.numeric(df_platform$Platform)
# Create logistic model
mdl_platform <- glm(Platform ~ JP_Sales, data = df_platform, family = binomial())
ggplot(df_platform, aes(x=JP_Sales, y=Platform)) + 
  geom_point(alpha=.2, color="Blue") +
  stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
  labs(
    x = "Sales (million)",
    y = "1=Wii / 0=DS",
    title = "Probability that a game is from Wii based on sales in Japan"
  )
`geom_smooth()` using formula 'y ~ x'

Anhand des Plots ist zu sehen, dass ein Spiel mit über 2 mio Verkäufe eher über das Platform Wii verkauft wurde. Dies deutet an, dass das Wii beliebter als das Nintendo DS ist.

Fazit

Bei den meisten Genren ist es nicht möglich die Verkäufe in Europa anhand der Verkäufe in Nord Amerika vorherzusagen. Wir haben jedoch einige Genren gefunden, bei denen Vorhersagen möglich ist: - Racing - Role-Playing - Simulation - Sport

Sehr interessant zu sehen war die Genre Simulation. Unser Modell, die bei 92% Genauigkeit liegt sagt voraus, dass ein beliebiges Spiel in der EU besser verkauft wird als in Nord Amerika. Bei allen anderen Genren verkaufen sich die Spiele in Nord Amerika besser.

LS0tDQp0aXRsZTogIlJlZ3Jlc3Npb24gbW9kZWxzIHdpdGggUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkRhcyBaaWVsIGlzdCBlcywgYXVzIGRlbSBEYXRhY2FtcCBEYXRlbnNhdHogW1ZpZGVvIEdhbWUgU2FsZXMgRGF0ZV0oaHR0cHM6Ly9hcHAuZGF0YWNhbXAuY29tL3dvcmtzcGFjZS9kYXRhc2V0cy9kYXRhc2V0LXB5dGhvbi12aWRlby1nYW1lcy1zYWxlcykgZm9sZ2VuZGUgRnJhZ2VzdGVsbHVuZyAvIEh5cG90aGVzZSB6dSBiZWFudHdvcnRlbjoNCg0KDQojIyMgRnJhZ2VzdGVsbHVuZzogIklzdCBlcyB3YWhyc2NoZWlubGljaGVyLCBkYXNzIHNpY2ggYmVzdGltbXRlIFNwaWVsZ2VucmVzIGluIEV1cm9wYSBzaWduaWZpa2FudCAoVW50ZXJzY2hpZWQgdm9uIDUwJSkgYmVzc2VyIHZlcmthdWZlbiBsYXNzZW4gYWxzIGltIEphcGFuaXNjaGVuIHVuZCBOb3JkYW1lcmlrYW5pc2NoZW4gTWFya3Q/Ig0KDQoNCkFscyBFaW5mw7xocnVuZyB3ZXJkZW4gd2lyIGF1ZiBEYXRhY2FtcCBmb2xnZW5kZSBLdXJzZSBkdXJjaGdlaGVuOg0KDQotIFtJbnRyb2R1Y3Rpb24gdG8gUmVncmVzc2lvbiBpbiBSXShodHRwczovL2FwcC5kYXRhY2FtcC5jb20vbGVhcm4vY291cnNlcy9pbnRyb2R1Y3Rpb24tdG8tcmVncmVzc2lvbi1pbi1yKQ0KDQotIFtJbnRlcm1lZGlhdGUgUmVncmVzc2lvbiBpbiBSXShodHRwczovL2FwcC5kYXRhY2FtcC5jb20vbGVhcm4vY291cnNlcy9pbnRlcm1lZGlhdGUtcmVncmVzc2lvbi1pbi1yKQ0KDQoNCmBgYHtyfQ0KIyBJbXBvcnQgbGlicmFyaWVzDQpsaWJyYXJ5KCJwbG90bHkiKQ0KbGlicmFyeSgiZ2dwbG90MiIpDQpsaWJyYXJ5KCJwbHlyIikNCmxpYnJhcnkoImRwbHlyIikNCmxpYnJhcnkoImJyb29tIikNCmxpYnJhcnkoImdyaWRFeHRyYSIpICAgDQpgYGANCg0KYGBge3J9DQojIFJlYWQgY3N2IGZyb20gZm9sZGVyICJkYXRhIg0KZGYgPSByZWFkLmNzdigiLi9kYXRhL3ZpZGVvX2dhbWVzX2RhdGEuY3N2IikNCg0KaGVhZChkZiwgMTApDQpgYGANCiMjIyBEYXRhIFdyYW5nbGluZw0KQmV2b3Igd2lyIG1pdCBkZW4gVmlzdWFsaXNpZXJ1bmdlbiB1bmQgTW9kZWxsZSBiZWdpbm5lbiBrw7ZubmVuLCBtw7xzc2VuIHdpciBkaWUgRGF0ZW4gc8OkdWJlcm4uIERhcyBoZWlzc3QgZXMgc29sbHRlIGtlaW5lIER1cGxpa2F0ZSBnZWJlbiwgZmVobGVuZGUgV2VydGUgc29sbHRlbiBrb3JyZWt0IGVpbmdldHJhZ2VuIHdlcmRlbiB1bmQgRGF0ZW4sIGRpZSBuaWNodCB2ZXJ3ZW5kZXQgd2VyZGVuIHNvbGx0ZW4gZ2Vsw7ZzY2h0IHdlcmRlbi4NCg0KRXMgaGF0ICJOL0EiIFdlcnRlIGluIGRlbiBTcGFsdGVuICJZZWFyIiB1bmQgIlB1Ymxpc2hlciIuIERpZXNlIFdlcnRlIHNvbGx0ZW4ga29ycmVrdGUgIk5BIiBXZXJ0ZSBzZWluLCBkYW1pdCBzaWUgYmVpIGRlbiBWaXN1YWxpc2llcnVuZ2VuIHVuZCBCZXJlY2hudW5nZW4gbmljaHQgYmVyw7xja3NpY2h0aWd0IHdlcmRlbi4NCg0KYGBge3J9DQojIFNob3cgcm93cyB3aXRoICJOL0EiIHZhbHVlcw0KZGZbZ3JlcCgiTi9BIiwgZGYkUHVibGlzaGVyKSxdDQpkZltncmVwKCJOL0EiLCBkZiRZZWFyKSxdDQpgYGANCg0KYGBge3J9DQojIFJlcGxhY2UgIk4vQSIgd2l0aCAiTkEiDQpkZltkZiA9PSAiTi9BIl0gPC0gTkENCg0KZGYgPC0gZGYgJT4lDQogIGZpbHRlcihkZiRHbG9iYWxfU2FsZXMgPiAwLjEpDQoNCmRmDQpgYGANCg0KYGBge3J9DQojIENoZWNrIGlmIHZhbHVlcyBoYXZlIGJlZW4gY29udmVydGVkDQpkZiAlPiUgDQogIHN1bW1hcml6ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+c3VtKGlzLm5hKC4pKSkpDQpgYGANCkRhIDIwMTcgbnVyIDMgRWludHLDpGdlIHVuZCAyMDIwIG51ciAxIEVpbnRyYWcgYmVpbmhhbHRldCwgd2VyZGVuIHdpciBkaWVzZSBKYWhyZW4gbmljaHQgYmVyw7xja3NpY2h0aWdlbiB1bmQgYXVzIGRlbSBEYXRhZnJhbWUgbMO2c2NoZW4sIHdlaWwgc2llIG5pY2h0IHZvbGxzdMOkbmRpZyBzaW5kIHVuZCBzbyBrw7ZubnRlbiB1bnNlcmUgTW9kZWxsZSB1bmdlbmF1IHdlcmRlbi4NCg0KYGBge3J9DQojIFJlbW92ZSB5ZWFycyAyMDE3IGFuZCAyMDIwIGZyb20gZGF0YXNldA0KZGZfY2xlYW4gPC0gZGZbIShkZiRZZWFyID09ICIyMDE3IiB8IGRmJFllYXIgPT0gIjIwMjAiKSxdDQoNCiMgUmVtb3ZlIHVubmVjZXNzYXJ5IGNvbHVtbnMgYmVjYXVzZSB0aGV5IGFyZSBub3QgbmVlZGVkIGZvciBvdXIgdGhlc2lzDQpkcm9wIDwtIGMoIlJhbmsiKQ0KZGZfY2xlYW4gPC0gZGZfY2xlYW5bLCEobmFtZXMoZGZfY2xlYW4pICVpbiUgZHJvcCldDQoNCmRmX2NsZWFuDQpgYGANCg0KYGBge3J9DQojIFNldCBkYXRhIHRvIGNvcnJlY3QgdHlwZQ0KZGZfY2xlYW4kR2VucmUgPC0gYXMuZmFjdG9yKGRmX2NsZWFuJEdlbnJlKQ0KZGZfY2xlYW4kWWVhciA8LSBhcy5udW1lcmljKGRmX2NsZWFuJFllYXIpDQpgYGANCg0KIyMjIEVyc3RlIFBsb3RzIGVyc3RlbGxlbg0KDQpgYGB7cn0NCm5hIDwtIHN1bShkZl9jbGVhblssICdOQV9TYWxlcyddLCBuYS5ybSA9IFRSVUUpDQpldSA8LSBzdW0oZGZfY2xlYW5bLCAnRVVfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQ0KanAgPC0gc3VtKGRmX2NsZWFuWywgJ0pQX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkNCm8gPC0gc3VtKGRmX2NsZWFuWywgJ090aGVyX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkNCmcgPC0gc3VtKGRmX2NsZWFuWywgJ0dsb2JhbF9TYWxlcyddLCBuYS5ybSA9IFRSVUUpDQoNCmZpZyA8LSBwbG90X2x5KA0KICB5ID0gYyhuYSwgZXUsIGpwLCBvKSwgDQogIHggPSBjKCJOb3J0aCBBbWVyaWNhIiwgIkV1cm9wZSIsICJKYXBhbiIsICJPdGhlciIpLCANCiAgdHlwZSA9ICdiYXInLA0KICB3aWR0aCA9IDgwMA0KKQ0KDQpmaWcgPC0gZmlnICU+JSBsYXlvdXQodGl0bGUgPSAiVmlkZW8gR2FtZSBTYWxlcyBPdmVydmlldyIsDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUmVnaW9uIiksDQogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiU2FsZXMgKG1pbGxpb24pIikpDQoNCmZpZw0KYGBgDQpXaXIgc2VoZW4sIGRhc3MgTm9yZCBBbWVyaWthIGRlciBncsO2c3N0ZSBNYXJrdCBpc3QuIA0KDQpgYGB7cn0NCiMgR3JvdXAgYnkgZ2VucmUgYW5kIHN1bW1hcml6ZSBnYW1lIHNhbGVzIHRvIGVhY2ggcmVnaW9uDQpkZl9nZW5yZSA8LSBkZl9jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoR2VucmUpICU+JQ0KICBzdW1tYXJpemUoDQogICAgTkFfU2FsZXNfU3VtID0gc3VtKE5BX1NhbGVzKSwNCiAgICBFVV9TYWxlc19TdW0gPSBzdW0oRVVfU2FsZXMpLCANCiAgICBKUF9TYWxlc19TdW0gPSBzdW0oSlBfU2FsZXMpLA0KICAgIE90aGVyX1NhbGVzX1N1bSA9IHN1bShPdGhlcl9TYWxlcyksDQogICAgR2xvYmFsX1NhbGVzX1N1bSA9IHN1bShHbG9iYWxfU2FsZXMpDQogICkNCg0KIyBQbG90IGdyb3VwZWQgYmFyIGNoYXJ0IHZpZGVvIGdhbWUgc2FsZXMgYnkgZ2VucmUNCmZpZyA8LSBwbG90X2x5KA0KICBkZl9nZW5yZSwgeSA9IH5HZW5yZSwgeCA9IH5OQV9TYWxlc19TdW0sIHR5cGUgPSAiYmFyIiwgbmFtZSA9ICJOb3J0aCBBbWVyaWNhIiwgd2lkdGggPSAxMDAwLCBoZWlnaHQgPSA4MDApICU+JSANCiAgYWRkX3RyYWNlKHggPSB+RVVfU2FsZXNfU3VtLCBuYW1lID0gIkV1cm9wZSIpICU+JQ0KICBhZGRfdHJhY2UoeCA9IH5KUF9TYWxlc19TdW0sIG5hbWUgPSAiSmFwYW4iKSAlPiUNCiAgYWRkX3RyYWNlKHggPSB+T3RoZXJfU2FsZXNfU3VtLCBuYW1lID0gIk90aGVyIikgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJWaWRlbyBHYW1lIFNhbGVzIGJ5IEdlbnJlIiwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiU2FsZXMgKG1pbGxpb24pIiksDQogICAgYmFybW9kZSA9ICJncm91cCINCiAgKQ0KDQpmaWcNCmBgYA0KSmFwYW4gaGF0IHZpZWwgbWVociBSb2xlLVBsYXksIFN0cmF0ZWdpZSB1bmQgdmllbCB3ZW5pZ2VyIFNob290ZXIgdW5kIEFjdGlvbiBhbHMgZGllIGFuZGVyZW4gUmVnaW9uZW4NCg0KYGBge3J9DQpkZl95ZWFyIDwtIGRmX2NsZWFuICU+JQ0KICBncm91cF9ieShZZWFyKSAlPiUNCiAgc3VtbWFyaXplKA0KICAgIE5BX1NhbGVzX1N1bSA9IHN1bShOQV9TYWxlcyksDQogICAgRVVfU2FsZXNfU3VtID0gc3VtKEVVX1NhbGVzKSwgDQogICAgSlBfU2FsZXNfU3VtID0gc3VtKEpQX1NhbGVzKSwNCiAgICBPdGhlcl9TYWxlc19TdW0gPSBzdW0oT3RoZXJfU2FsZXMpLA0KICAgIEdsb2JhbF9TYWxlc19TdW0gPSBzdW0oR2xvYmFsX1NhbGVzKQ0KICApDQoNCmZpZyA8LSBwbG90X2x5KA0KICBkZl95ZWFyLCB5ID0gfk5BX1NhbGVzX1N1bSwgeCA9IH5ZZWFyLCB0eXBlID0gImJhciIsIG5hbWUgPSAiTm9ydGggQW1lcmljYSIsIHdpZHRoID0gOTAwLCBoZWlnaHQgPSA1MDApICU+JSANCiAgYWRkX3RyYWNlKHkgPSB+RVVfU2FsZXNfU3VtLCBuYW1lID0gIkV1cm9wZSIpICU+JQ0KICBhZGRfdHJhY2UoeSA9IH5KUF9TYWxlc19TdW0sIG5hbWUgPSAiSmFwYW4iKSAlPiUNCiAgYWRkX3RyYWNlKHkgPSB+T3RoZXJfU2FsZXNfU3VtLCBuYW1lID0gIk90aGVyIikgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJWaWRlbyBHYW1lIGZyb20gU2FsZXMgYnkgWWVhciIsDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlllYXIiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiU2FsZXMgKG1pbGxpb24pIiksDQogICAgYmFybW9kZSA9ICJzdGFjayINCiAgKQ0KDQpmaWcNCmBgYA0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIHNhbGVzIG9mIGVhY2ggZ2VucmUgZnJvbSBlYWNoIHJlZ2lvbg0KZGZfc2FsZXNfYXZnIDwtIGRmX2NsZWFuICU+JQ0KICBncm91cF9ieShHZW5yZSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBFVV9TYWxlc19BdmcgPSBtZWFuKEVVX1NhbGVzKSwNCiAgICBOQV9TYWxlc19BdmcgPSBtZWFuKE5BX1NhbGVzKSwNCiAgICBKUF9TYWxlc19BdmcgPSBtZWFuKEpQX1NhbGVzKSwNCiAgICBPdGhlcl9TYWxlc19BdmcgPSBtZWFuKE90aGVyX1NhbGVzKSwNCiAgICBHbG9iYWxfU2FsZXNfQXZnID0gbWVhbihHbG9iYWxfU2FsZXMpKQ0KDQpkZl9zYWxlc19hdmcNCmBgYA0KSGllciBzZWhlbiB3aXIgZGllIGR1cmNoc2Nobml0dGxpY2hlIEFuemhhbCB2b24gVmVya8OkdWZlIGFsbGVyIEdlbnJlDQoNCiMjIFJlZ3Jlc3Npb25zbW9kZWxsZQ0KDQpEYSB3aXIgdW5zZXJlIERhdGVuIGpldHp0IGJlc3NlciB2ZXJzdGVoZW4sIGvDtm5uZW4gd2lyIG1pdCBkZW4gUmVncmVzc2lvbnNtb2RlbGxlbiB1bmQgbWl0IGRlciBCZWFudHdvcnR1bmcgdW5zZXJlciBGcmFnZXN0ZWxsdW5nIGJlZ2lubmVuLg0KDQojIyMgS2FubiBtYW4gYW5oYW5kIGRlciBub3JkYW1lcmlrYW5pc2NoZW4gVmVya8OkdWZlIHZvcmF1c3NhZ2VuLCB3aWUgc2ljaCBlaW4gR2VucmUgaW0gZXVyb3DDpGlzY2hlbiBNYXJrdCB2ZXJrYXVmZW4gd2lyZD8NCg0KRGEgZXMgZWluaWdlIEF1c3JlaXNzZXIgZ2lidCwgei5CIHdlbm4gc2ljaCBlaW4gU3BpZWwgaW4gZWluZXIgUmVnaW9uIGdhciBuaWNodCB2ZXJrYXVmdCB3dXJkZSwgaGFiZW4gd2lyIGVpbmVuIEZpbHRlciBlaW5nZWJhdXQsIHdlbGNoZSBTYWxlcyB1bnRlciAxLjAgbmljaHQgYmVyw7xja3NpY2h0aWd0Lg0KDQpVbnRlbiBoYWJlbiB3aXIgZGllIGJlc3RlbiBGaXRzIGdlbm9tbWVuLCBkYSBiZWkgZGVuIGFuZGVyZW4gU3BpZWxnZW5yZXMgZGVyIHItc3F1YXJlZCBXZXJ0IHVudGVyIDUwJSBsYWcuIERvcnQgaXN0IGFsc28ga2VpbiBadXNhbW1lbmhhbmcgenUgZXJrZW5uZW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBSYWNpbmcgZ2FtZXMNCmRmX3JhY2luZyA8LSBkZl9jbGVhbiAlPiUNCiAgZmlsdGVyKA0KICAgIEdlbnJlID09ICJSYWNpbmciLA0KICAgIE5BX1NhbGVzID4gMS4wMCwNCiAgICBFVV9TYWxlcyA+IDEuMDANCiAgKQ0KDQpnZ3Bsb3QoZGZfcmFjaW5nLCBhZXMoeD1FVV9TYWxlcywgeT1OQV9TYWxlcykpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgKw0KICBsYWJzKHRpdGxlID0gIlJhY2luZyBHYW1lIFNhbGVzIE92ZXJ2aWV3IikNCmBgYA0KYGBge3J9DQojIENyZWF0ZSBsaW5lYXIgbW9kZWwNCm1kbF9yYWNpbmcgPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3JhY2luZykNCg0KIyBFeHRyYWN0IG1vZGVsIHNjb3JlDQptZGxfcmFjaW5nICU+JQ0KICBnbGFuY2UoKSAlPiUNCiAgcHVsbChyLnNxdWFyZWQpDQoNCiMgUHJlZGljdCBFVSBTYWxlcyBmb3IgYSBSYWNpbmcgR2FtZSBiYXNlZCBvbiBOQSBTYWxlcw0KcHJlZGljdF9yYWNpbmcgPC0gdGliYmxlKE5BX1NhbGVzID0gNSkNCnByZWRpY3QobWRsX3JhY2luZywgcHJlZGljdF9yYWNpbmcpDQpgYGANCldlbm4gZWluIFJhY2luZyBHYW1lIGluIE5vcmQgQW1lcmlrYSA1IE1pbGxpb25lbiBWZXJrw6R1ZmUgYXVmd2Vpc3QsIGxpZWd0IGRpZSBWZXJrYXVmcy1Wb3JoZXJzYWdlIGbDvHIgRXVyb3BhIGJlaSBydW5kIDMuOSBNaWxsaW9uZW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBSb2xlLVBsYXlpbmcgZ2FtZXMNCmRmX3JvbGUgPC0gZGZfY2xlYW4gJT4lDQogIGZpbHRlcigNCiAgICBHZW5yZSA9PSAiUm9sZS1QbGF5aW5nIiwNCiAgICBOQV9TYWxlcyA+IDEuMDAsDQogICAgRVVfU2FsZXMgPiAxLjAwDQogICkNCg0KZ2dwbG90KGRmX3JvbGUsIGFlcyh4PUVVX1NhbGVzLCB5PU5BX1NhbGVzKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKSArDQogIGxhYnModGl0bGUgPSAiUm9sZS1QbGF5aW5nIEdhbWUgU2FsZXMgT3ZlcnZpZXciKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGxpbmVhciBtb2RlbA0KbWRsX3JwIDwtIGxtKEVVX1NhbGVzIH4gTkFfU2FsZXMsIGRhdGEgPSBkZl9yb2xlKQ0KDQojIEV4dHJhY3QgbW9kZWwgc2NvcmUNCm1kbF9ycCAlPiUNCiAgZ2xhbmNlKCkgJT4lDQogIHB1bGwoci5zcXVhcmVkKQ0KDQojIFByZWRpY3QgRVUgU2FsZXMgZm9yIGEgUm9sZS1QbGF5aW5nIEdhbWUgYmFzZWQgb24gTkEgU2FsZXMNCnByZWRpY3RfcnAgPC0gdGliYmxlKE5BX1NhbGVzID0gNSkNCnByZWRpY3QobWRsX3JwLCBwcmVkaWN0X3JwKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIERhdGFGcmFtZSBvbmx5IHdpdGggU2hvb3RlciBnYW1lcw0KZGZfc2hvb3RlciA8LSBkZl9jbGVhbiAlPiUNCiAgZmlsdGVyKA0KICAgIEdlbnJlID09ICJTaG9vdGVyIiwNCiAgICBOQV9TYWxlcyA+IDAuMDAsDQogICAgRVVfU2FsZXMgPiAwLjAwDQogICkNCg0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBTaG9vdGVyIGdhbWVzIHdpdGhvdXQgRHVjayBIdW50DQpkZl9zaG9vdGVyX25vX2R1Y2todW50IDwtIGRmX2NsZWFuICU+JQ0KICBmaWx0ZXIoDQogICAgR2VucmUgPT0gIlNob290ZXIiLA0KICAgIE5BX1NhbGVzID4gMC4wMCwNCiAgICBOYW1lICE9ICJEdWNrIEh1bnQiLA0KICAgIEVVX1NhbGVzID4gMC4wMA0KICApDQoNCiMgQ3JlYXRlIHN1YnBsb3RzDQpwMSA8LSBnZ3Bsb3QoZGZfc2hvb3RlciwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJTaG9vdGVyIEdhbWUgU2FsZXMiKQ0KDQpwMiA8LSBnZ3Bsb3QoZGZfc2hvb3Rlcl9ub19kdWNraHVudCwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJTaG9vdGVyIEdhbWUgU2FsZXMgbm8gRHVjayBIdW50IikNCg0KIyBQbG90IGJvdGggcGxvdHMgc2lkZSBieSBzaWRlDQpncmlkLmFycmFuZ2UocDEsIHAyLCBuY29sID0gMikgIA0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgbGluZWFyIG1vZGVsDQptZGxfc2hvb3RlciA8LSBsbShFVV9TYWxlcyB+IE5BX1NhbGVzLCBkYXRhID0gZGZfc2hvb3RlcikNCg0KIyBFeHRyYWN0IG1vZGVsIHNjb3JlDQptZGxfc2hvb3RlciAlPiUNCiAgZ2xhbmNlKCkgJT4lDQogIHB1bGwoci5zcXVhcmVkKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGxpbmVhciBtb2RlbA0KbWRsX3Nob290ZXJfbm9fZHVja2h1bnQgPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3Nob290ZXJfbm9fZHVja2h1bnQpDQoNCiMgRXh0cmFjdCBtb2RlbCBzY29yZQ0KbWRsX3Nob290ZXJfbm9fZHVja2h1bnQgJT4lDQogIGdsYW5jZSgpICU+JQ0KICBwdWxsKHIuc3F1YXJlZCkNCmBgYA0KV2llIHdpciBoaWVyIGFuaGFuZCBkZXIgUi1TcXVhcmVkIHNlaGVuLCBmaXR0ZWQgZGFzIE1vZGVsbCB2aWVsIGJlc3NlciBvaG5lIER1Y2sgSHVudC4gRGllcyBsaWVndCBkYXJhbiwgZGFzcyBkaWVzZXMgU3BpZWwgaW4gTm9yZCBBbWVyaWthIHp1c2FtbWVuIG1pdCBkZXIgS29uc29sZSB2ZXJrYXVmdCB3dXJkZSwgbmljaHQgd2llIGluIEV1cm9wYS4gRGFydW0gaXN0IGRpZXMgZsO8ciB1bnMgZWluZW4gQXVzcmVpc3NlciB1bmQgd2lyIHdvbGx0ZW4gZGllc2UgR2VucmUgb2huZSBBdXNyZWlzc2VyIHZpc3VhbGlzaWVyZW4gdW5kIG1vZGVsbGllcmVuLg0KDQpBYmVyIGRhIGRhcyBNb2RlbGwgYXVjaCBvaG5lIER1Y2sgSHVudCBuaWNodCBndXQgc2VociBndXQgaXN0LCB3ZXJkZW4gd2lyIG1pdCBkaWVzZW0gR2VucmUga2VpbmUgVm9yaGVyc2FnZW4gZHVyY2hmw7xocmVuLg0KDQpgYGB7cn0NCiMgQ3JlYXRlIERhdGFGcmFtZSBvbmx5IHdpdGggU2ltdWxhdGlvbiBnYW1lcw0KZGZfc2ltIDwtIGRmX2NsZWFuICU+JQ0KICBmaWx0ZXIoDQogICAgR2VucmUgPT0gIlNpbXVsYXRpb24iLA0KICAgIE5BX1NhbGVzID4gMS4wMCwNCiAgICBFVV9TYWxlcyA+IDEuMDANCikNCg0KZ2dwbG90KGRmX3NpbSwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJTaW11bGF0aW9uIEdhbWUgU2FsZXMgT3ZlcnZpZXciKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgbGluZWFyIG1vZGVsDQptZGxfc2ltIDwtIGxtKEVVX1NhbGVzIH4gTkFfU2FsZXMsIGRhdGEgPSBkZl9zaW0pDQoNCiMgRXh0cmFjdCBtb2RlbCBzY29yZQ0KbWRsX3NpbSAlPiUNCiAgZ2xhbmNlKCkgJT4lDQogIHB1bGwoci5zcXVhcmVkKQ0KDQojIFByZWRpY3QgRVUgU2FsZXMgZm9yIGEgU2ltdWxhdGlvbiBHYW1lIGJhc2VkIG9uIE5BIFNhbGVzDQpwcmVkaWN0X3NpbSA8LSB0aWJibGUoTkFfU2FsZXMgPSA1KQ0KcHJlZGljdChtZGxfc2ltLCBwcmVkaWN0X3NpbSkNCmBgYA0KV2VubiBlaW4gU2ltdWxhdGlvbiBHYW1lIGluIE5BIDUgTWlsbGlvbiBWZXJrw6R1ZmUgYXVmd2Vpc3QsIGxpZWd0IGRpZSBWb3JoZXJzYWdlIGbDvHIgRVUgYmVpIDUuNCBNaWxsaW9uZW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBTcG9ydCBnYW1lcw0KZGZfc3BvcnQgPC0gZGZfY2xlYW4gJT4lDQogIGZpbHRlcigNCiAgICBHZW5yZSA9PSAiU3BvcnRzIiwNCiAgICBOQV9TYWxlcyA+IDEuMDAsDQogICAgRVVfU2FsZXMgPiAxLjAwDQogICkNCg0KZ2dwbG90KGRmX3Nwb3J0LCBhZXMoeD1FVV9TYWxlcywgeT1OQV9TYWxlcykpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgKw0KICBsYWJzKHRpdGxlID0gIlNwb3J0IEdhbWUgU2FsZXMgT3ZlcnZpZXciKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGxpbmVhciBtb2RlbA0KbWRsX3Nwb3J0IDwtIGxtKEVVX1NhbGVzIH4gTkFfU2FsZXMsIGRhdGEgPSBkZl9zcG9ydCkNCg0KIyBFeHRyYWN0IG1vZGVsIHNjb3JlDQptZGxfc3BvcnQgJT4lDQogIGdsYW5jZSgpICU+JQ0KICBwdWxsKHIuc3F1YXJlZCkNCg0KIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFNwb3J0IEdhbWUgYmFzZWQgb24gTkEgU2FsZXMNCnByZWRpY3Rfc3BvcnQgPC0gdGliYmxlKE5BX1NhbGVzID0gNSkNCnByZWRpY3QobWRsX3Nwb3J0LCBwcmVkaWN0X3Nwb3J0KQ0KYGBgDQpXZW5uIGVpbiBTcG9ydCBHYW1lIGluIE5BIDUgTWlsbGlvbiBWZXJrw6R1ZmUgYXVmd2Vpc3QsIGxpZWd0IGRpZSBWb3JoZXJzYWdlIGbDvHIgRVUgYmVpIDQgTWlsbGlvbmVuLg0KDQojIyMgUmVzaWR1ZW5hbmFseXNlICh6dW0gYmV1cnRlaWxlbiBvYiBkYXMgTW9kZWxsIGd1dCBpc3QpDQotIFJlc2lkdWVuIGVyd2FydHVuZ3N3ZXJ0IHZvbiAwDQotIFJlc2lkdWVuIHNpbmQgdm9uZWluYW5kZXIgdW5hYmjDpG5naWcNCi0gUmVzaWR1ZW4gc2luZCBub3JtYWx2ZXJ0ZWlsdA0KDQpgYGB7cn0NCiMgQ3JlYXRlIFJlc2lkdWFsIFNjYXR0ZXJwbG90DQoNCmRmIDwtIGF1Z21lbnQobWRsX3JhY2luZykNCg0KZ2dwbG90KGRmLCBhZXMoeCA9IDE6bnJvdyhkZiksIHkgPSAucmVzaWQpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsNCiAgZ2d0aXRsZSgiUmVzaWR1YWxzIE1vZGVsIFJhY2luZyBHZW5yZSIpICsNCiAgeGxhYigiIikNCmBgYA0KYGBge3J9DQojIENyZWF0ZSBSZXNpZHVhbCBIaXN0b2dyYW0gKHRvIHNlZSBpZiB0aGUgZGF0YSBpcyBhIG5vcm1hbCBkaXN0cmlidXRpb24pDQoNCmdncGxvdChkZl9yYWNpbmcsIGFlcyh4ID0gbWRsX3JhY2luZyRyZXNpZHVhbHMpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsNCiAgZ2d0aXRsZSgiUmVzaWR1YWxzIE1vZGVsIFJhY2luZyBHZW5yZSIpICsNCiAgeGxhYigicmVzaWR1YWxzIikNCmBgYA0KRGFzIEhpc3RvZ3JhbW0gaXN0IG5pY2h0IGdhbnogZ2VuYXUgd2llIGVpbmUgTm9ybWFsdmVydGVpbHVuZyBhYmVyIGvDtm5udGUgdHJvdHpkZW0gYWxzIGVpbmUgYWt6ZXB0aWVydCBzZWluLg0KDQpgYGB7cn0NCiMgQ3JlYXRlIFJlc2lkdWFsIFNjYXR0ZXJwbG90DQoNCmRmIDwtIGF1Z21lbnQobWRsX3JwKQ0KDQpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBjb2xvcj0iUmVkIikgKw0KICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUm9sZS1QbGF5aW5nIEdlbnJlIikgKw0KICB4bGFiKCIiKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIFJlc2lkdWFsIEhpc3RvZ3JhbSAodG8gc2VlIGlmIHRoZSBkYXRhIGlzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbikNCg0KZ2dwbG90KGRmX3JvbGUsIGFlcyh4ID0gbWRsX3JwJHJlc2lkdWFscykpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKw0KICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiUmVkIikgKw0KICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgUm9sZS1QbGF5aW5nIEdlbnJlIikgKw0KICB4bGFiKCJyZXNpZHVhbHMiKQ0KYGBgDQpEYXMgSGlzdG9ncmFtbSBpc3QgbmljaHQgZ2FueiBnZW5hdSB3aWUgZWluZSBOb3JtYWx2ZXJ0ZWlsdW5nIGFiZXIga8O2bm50ZSB0cm90emRlbSBhbHMgZWluZSBha3plcHRpZXJ0IHNlaW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgUmVzaWR1YWwgU2NhdHRlcnBsb3QNCg0KZGYgPC0gYXVnbWVudChtZGxfc2ltKQ0KDQpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBjb2xvcj0iUmVkIikgKw0KICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU2ltdWxhdGlvbiBHZW5yZSIpICsNCiAgeGxhYigiIikNCmBgYA0KYGBge3J9DQojIENyZWF0ZSBSZXNpZHVhbCBIaXN0b2dyYW0gKHRvIHNlZSBpZiB0aGUgZGF0YSBpcyBhIG5vcm1hbCBkaXN0cmlidXRpb24pDQoNCmdncGxvdChkZl9zaW0sIGFlcyh4ID0gbWRsX3NpbSRyZXNpZHVhbHMpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsNCiAgZ2d0aXRsZSgiUmVzaWR1YWxzIE1vZGVsIFNpbXVsYXRpb24gR2VucmUiKSArDQogIHhsYWIoInJlc2lkdWFscyIpDQpgYGANCkRhcyBIaXN0b2dyYW1tIGlzdCBuaWNodCBnYW56IGdlbmF1IHdpZSBlaW5lIE5vcm1hbHZlcnRlaWx1bmcgYWJlciBrw7ZubnRlIHRyb3R6ZGVtIGFscyBlaW5lIGFremVwdGllcnQgc2Vpbi4NCg0KDQpgYGB7cn0NCiMgQ3JlYXRlIFJlc2lkdWFsIFNjYXR0ZXJwbG90DQoNCmRmIDwtIGF1Z21lbnQobWRsX3Nwb3J0KQ0KDQpnZ3Bsb3QoZGYsIGFlcyh4ID0gMTpucm93KGRmKSwgeSA9IC5yZXNpZCkpICsgDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBjb2xvcj0iUmVkIikgKw0KICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU3BvcnQgR2VucmUiKSArDQogIHhsYWIoIiIpDQpgYGANCmBgYHtyfQ0KIyBDcmVhdGUgUmVzaWR1YWwgSGlzdG9ncmFtICh0byBzZWUgaWYgdGhlIGRhdGEgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uKQ0KDQpnZ3Bsb3QoZGZfc3BvcnQsIGFlcyh4ID0gbWRsX3Nwb3J0JHJlc2lkdWFscykpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCkgKw0KICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiUmVkIikgKw0KICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU3BvcnQgR2VucmUiKSArDQogIHhsYWIoInJlc2lkdWFscyIpDQpgYGANCkRhcyBIaXN0b2dyYW1tIGlzdCBuaWNodCBnYW56IGdlbmF1IHdpZSBlaW5lIE5vcm1hbHZlcnRlaWx1bmcgYWJlciBrw7ZubnRlIHRyb3R6ZGVtIGFscyBlaW5lIGFremVwdGllcnQgc2Vpbi4NCg0KIyMjIEhvdyBjbG9zZWx5IHJlbGF0ZWQgYXJlIE5vcnRoIEFtZXJpY2FuIGFuZCBFdXJvcGVhbiB2aWRlbyBnYW1lIHNhbGVzPyBIb3cgZG8gc2FsZXMgaW4gSmFwYW4gY29tcGFyZSB0byBOb3J0aCBBbWVyaWNhIGFuZCBFdXJvcGU/DQpgYGB7cn0NCiMgQ29sb3IgdGhlIFNQTE9NIG9mIE5BX1NhbGVzLCBFVV9TYWxlcywgYW5kIEpQX1NhbGVzIGJ5IG5pbnRlbmRvDQpkZl9jbGVhbiAlPiUNCiAgcGxvdF9seShjb2xvciA9IH5HZW5yZSkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgdHlwZSA9ICdzcGxvbScsDQogICAgZGltZW5zaW9ucyA9IGxpc3QoDQogICAgICBsaXN0KGxhYmVsID0gJ04uIEFtZXJpY2EnLCB2YWx1ZXMgPSB+TkFfU2FsZXMpLA0KICAgICAgbGlzdChsYWJlbCA9ICdFdXJvcGUnLCB2YWx1ZXMgPSB+RVVfU2FsZXMpLCAgICANCiAgICAgIGxpc3QobGFiZWwgPSAnSmFwYW4nLCB2YWx1ZXMgPSB+SlBfU2FsZXMpICAgICAgIA0KICAgICkNCiAgKQ0KYGBgDQojIyMgTmludGVuZG8gU3BpZWxlIHZlcmthdWZlbiBzaWNoIGJlc3NlciBhbHMgRWxlY3Ryb25pYyBBcnRzIGF1ZiBkZXIgZ2FuemVuIFdlbHQuIA0KDQpNaXQgZGllc2VyIEZyYWdlc3RlbGx1bmcgd29sbGVuIHdpciBoZXJhdXNmaW5kZW4gb2Igc2ljaCBkaWUgTmludGVuZG8gS29uc29sZSBpbiBKYXBhbiBiZXNzZXIgdmVya2F1ZnQuDQpgYGB7cn0NCiMgQ3JlYXRlIERhdGFGcmFtZSB3aXRoIG9ubHkgTmludGVuZG8gYW5kIFNvbnkgYXMgUHVibGlzaGVyDQpwdWJsaXNoZXJzID0gYygiTmludGVuZG8iLCAiRWxlY3Ryb25pYyBBcnRzIikNCg0KZGZfcHVibGlzaGVyIDwtIGRmX2NsZWFuICU+JQ0KICBmaWx0ZXIoDQogICAgUHVibGlzaGVyICVpbiUgcHVibGlzaGVycw0KICAgICkNCg0KZGZfcHVibGlzaGVyDQpgYGANCmBgYHtyfQ0KIyBSZXBsYWNlIHB1Ymxpc2hlciBuYW1lIHdpdGggMCBhbmQgMQ0KZGZfcHVibGlzaGVyJFB1Ymxpc2hlcltkZl9wdWJsaXNoZXIkUHVibGlzaGVyID09ICJFbGVjdHJvbmljIEFydHMiXSA8LSAwDQpkZl9wdWJsaXNoZXIkUHVibGlzaGVyW2RmX3B1Ymxpc2hlciRQdWJsaXNoZXIgPT0gIk5pbnRlbmRvIl0gPC0gMQ0KDQojIFNhdmUgYXMgaW50DQpkZl9wdWJsaXNoZXIkUHVibGlzaGVyIDwtIGFzLm51bWVyaWMoZGZfcHVibGlzaGVyJFB1Ymxpc2hlcikNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGxvZ2lzdGljIG1vZGVsDQptZGxfcHVibGlzaGVyIDwtIGdsbShQdWJsaXNoZXIgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3B1Ymxpc2hlciwgZmFtaWx5ID0gYmlub21pYWwoKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZl9wdWJsaXNoZXIsIGFlcyh4PUdsb2JhbF9TYWxlcywgeT1QdWJsaXNoZXIpKSArIA0KICBnZW9tX3BvaW50KGFscGhhPS41LCBjb2xvcj0iQmx1ZSIpICsNCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJnbG0iLCBjb2wgPSAiUmVkIiwgc2U9RkFMU0UsIG1ldGhvZC5hcmdzID0gbGlzdChmYW1pbHk9Ymlub21pYWwpKSArDQogIGxhYnMoDQogICAgeCA9ICJTYWxlcyAobWlsbGlvbikiLA0KICAgIHkgPSAiMT1OaW50ZW5kbyAvIDA9RWxlY3Ryb25pYyBBcnRzIiwNCiAgICB0aXRsZSA9ICJQcm9iYWJpbGl0eSB0aGF0IGEgZ2FtZSBpcyBmcm9tIE5pbnRlbmRvIGJhc2VkIG9uIGdsb2JhbCBzYWxlcyINCiAgKQ0KYGBgDQpXaXIga8O2bm5lbiBoaWVyIGd1dCBzZWhlbiwgZGFzcyBOaW50ZW5kbyB2aWVsIGVyZm9sZ3JlaWNoZXJlIFNwaWVsZSBwcm9kdXppZXJ0IGhhYmVuLiBEaWVzIGRldXRldCBhdWNoIGFuLCBkYXNzIE5pbnRlbmRvIGJlbGllYnRlciBpc3QgYWxzIEVsZWN0cm9uaWMgQXJ0cw0KDQoNCiMjIyBXaWkgU3BpZWxlIHZlcmthdWZlbiBzaWNoIGJlc3NlciB3aWUgRFMgU3BpZWxlIGluIEphcGFuDQpgYGB7cn0NCiMgQ3JlYXRlIERhdGFGcmFtZSB3aXRoIG9ubHkgV2lpIGFuZCBEUyBhcyBQbGF0Zm9ybXMNCnBsYXRmb3JtcyA9IGMoIldpaSIsICJEUyIpDQoNCmRmX3BsYXRmb3JtIDwtIGRmX2NsZWFuICU+JQ0KICBmaWx0ZXIoDQogICAgUGxhdGZvcm0gJWluJSBwbGF0Zm9ybXMNCiAgICApDQoNCmRmX3BsYXRmb3JtDQpgYGANCmBgYHtyfQ0KIyBSZXBsYWNlIHBsYXRmb3JtIG5hbWUgd2l0aCAwIGFuZCAxDQpkZl9wbGF0Zm9ybSRQbGF0Zm9ybVtkZl9wbGF0Zm9ybSRQbGF0Zm9ybSA9PSAiV2lpIl0gPC0gMA0KZGZfcGxhdGZvcm0kUGxhdGZvcm1bZGZfcGxhdGZvcm0kUGxhdGZvcm0gPT0gIkRTIl0gPC0gMQ0KDQojIFNhdmUgYXMgaW50DQpkZl9wbGF0Zm9ybSRQbGF0Zm9ybSA8LSBhcy5udW1lcmljKGRmX3BsYXRmb3JtJFBsYXRmb3JtKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgbG9naXN0aWMgbW9kZWwNCm1kbF9wbGF0Zm9ybSA8LSBnbG0oUGxhdGZvcm0gfiBKUF9TYWxlcywgZGF0YSA9IGRmX3BsYXRmb3JtLCBmYW1pbHkgPSBiaW5vbWlhbCgpKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3BsYXRmb3JtLCBhZXMoeD1KUF9TYWxlcywgeT1QbGF0Zm9ybSkpICsgDQogIGdlb21fcG9pbnQoYWxwaGE9LjIsIGNvbG9yPSJCbHVlIikgKw0KICBzdGF0X3Ntb290aChtZXRob2Q9ImdsbSIsIGNvbCA9ICJSZWQiLCBzZT1GQUxTRSwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseT1iaW5vbWlhbCkpICsNCiAgbGFicygNCiAgICB4ID0gIlNhbGVzIChtaWxsaW9uKSIsDQogICAgeSA9ICIxPVdpaSAvIDA9RFMiLA0KICAgIHRpdGxlID0gIlByb2JhYmlsaXR5IHRoYXQgYSBnYW1lIGlzIGZyb20gV2lpIGJhc2VkIG9uIHNhbGVzIGluIEphcGFuIg0KICApDQpgYGANCkFuaGFuZCBkZXMgUGxvdHMgaXN0IHp1IHNlaGVuLCBkYXNzIGVpbiBTcGllbCBtaXQgw7xiZXIgMiBtaW8gVmVya8OkdWZlIGVoZXIgw7xiZXIgZGFzIFBsYXRmb3JtIFdpaSB2ZXJrYXVmdCB3dXJkZS4gRGllcyBkZXV0ZXQgYW4sIGRhc3MgZGFzIFdpaSBiZWxpZWJ0ZXIgYWxzIGRhcyBOaW50ZW5kbyBEUyBpc3QuDQoNCiMjIyBGYXppdA0KQmVpIGRlbiBtZWlzdGVuIEdlbnJlbiBpc3QgZXMgbmljaHQgbcO2Z2xpY2ggZGllIFZlcmvDpHVmZSBpbiBFdXJvcGEgYW5oYW5kIGRlciBWZXJrw6R1ZmUgaW4gTm9yZCBBbWVyaWthIHZvcmhlcnp1c2FnZW4uIFdpciBoYWJlbiBqZWRvY2ggZWluaWdlIEdlbnJlbiBnZWZ1bmRlbiwgYmVpIGRlbmVuIFZvcmhlcnNhZ2VuIG3DtmdsaWNoIGlzdDogDQotIFJhY2luZw0KLSBSb2xlLVBsYXlpbmcNCi0gU2ltdWxhdGlvbg0KLSBTcG9ydA0KDQpTZWhyIGludGVyZXNzYW50IHp1IHNlaGVuIHdhciBkaWUgR2VucmUgU2ltdWxhdGlvbi4gVW5zZXIgTW9kZWxsLCBkaWUgYmVpIDkyJSBHZW5hdWlna2VpdCBsaWVndCBzYWd0IHZvcmF1cywgZGFzcyBlaW4gYmVsaWViaWdlcyBTcGllbCBpbiBkZXIgRVUgYmVzc2VyIHZlcmthdWZ0IHdpcmQgYWxzIGluIE5vcmQgQW1lcmlrYS4gQmVpIGFsbGVuIGFuZGVyZW4gR2VucmVuIHZlcmthdWZlbiBzaWNoIGRpZSBTcGllbGUgaW4gTm9yZCBBbWVyaWthIGJlc3Nlci4=